package cn.vehicle.biz.core.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.vehicle.biz.cache.LastPlateDetectedResultCache;
import cn.vehicle.biz.core.PlateDetectedBiz;
import cn.vehicle.biz.core.support.PlateDetectSupport;
import cn.vehicle.biz.core.support.PlateDetectSupportFactory;
import cn.vehicle.biz.event.GIoInResultEvent;
import cn.vehicle.biz.event.PlateDetectResultEvent;
import cn.vehicle.constants.Constants;
import cn.vehicle.domains.ctx.PlateDetectContext;
import cn.vehicle.domains.entity.CameraEntity;
import cn.vehicle.domains.entity.CommonVehicleEntity;
import cn.vehicle.domains.info.*;
import cn.vehicle.enums.GIoInTypeEnum;
import cn.vehicle.enums.InOutTypeEnum;
import cn.vehicle.enums.WorkModelEnum;
import cn.vehicle.exception.VehicleAccessException;
import cn.vehicle.service.CameraService;
import cn.vehicle.service.CameraSnapshotService;
import cn.vehicle.service.CommonVehicleService;
import cn.vehicle.service.config.SystemConfig;
import cn.vehicle.service.config.SystemConfigService;
import cn.vehicle.utils.Base64ToImageUtil;
import cn.vehicle.utils.CameraSemaphore;
import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Map;
import java.util.Objects;


/**
 * 车牌识别业务实现类
 * <p>
 * 负责接收来自相机的车牌识别信息并执行后续业务逻辑：
 * <ul>
 *   <li>校验车牌有效性；</li>
 *   <li>加载车辆与相机信息；</li>
 *   <li>调用对应的进出场业务处理逻辑；</li>
 *   <li>记录台账信息。</li>
 * </ul>
 * </p>
 *
 * <p>说明：</p>
 * <ul>
 *   <li>支持多种车辆类型，通过 {@link PlateDetectSupportFactory} 动态获取具体策略；</li>
 *   <li>若台账记录失败，会抛出 {@link VehicleAccessException#LEDGER_ERROR} 异常。</li>
 * </ul>
 */
@Service
@Slf4j
public class PlateDetectedBizImpl implements PlateDetectedBiz {

    @Resource
    private CameraService cameraService;

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private CommonVehicleService commonVehicleService;

    @Resource
    private PlateDetectSupportFactory plateDetectSupportFactory;

    @Resource
    private CameraSnapshotService cameraSnapshotService;

    @Resource
    private ApplicationEventPublisher publisher;

    @Resource
    private LastPlateDetectedResultCache lastPlateDetectedResultCache;





    /**
     * 接收相机上传的车牌识别结果
     * <p>封装上下文信息并调用核心处理逻辑。</p>
     *
     * @param plateDetectedInternalInfo 相机上传的内部车牌识别信息
     * @return true 表示处理成功，false 表示处理失败
     */
    @Override
    public Boolean receivePlateDetected(PlateDetectedInternalInfo plateDetectedInternalInfo) {


        convertCameraIp(plateDetectedInternalInfo);

        //加锁 同一时间 相机只处理一个识别请求
        if (!CameraSemaphore.tryAcquire(plateDetectedInternalInfo.getInternalCameraInfo().getIp())) {
            throw VehicleAccessException.SYSTEM_ERROR.newInstance("相机处理中");
        }


        // 创建上下文对象，将识别信息拷贝到业务上下文中，便于后续统一传递
        PlateDetectContext plateDetectContext = new PlateDetectContext();
        BeanUtils.copyProperties(plateDetectedInternalInfo, plateDetectContext);

        // 提取识别到的车牌号
        String vehicleNumber = plateDetectedInternalInfo.getInternalVehicleInfo().getVehicleNumber();
        plateDetectContext.setVehicleNumber(vehicleNumber);

        // 校验车牌号是否在系统可识别范围内（过滤忽略规则）
        CommonVehicleEntity commonVehicleEntity = checkVehicleNumber(vehicleNumber);
        plateDetectContext.setCommonVehicleEntity(commonVehicleEntity);

        // 根据相机IP获取相机信息，用于识别来源追溯
        CameraEntity cameraEntity = cameraService.getCameraByIp(plateDetectedInternalInfo.getInternalCameraInfo().getIp());
        if (cameraEntity == null) {
            throw VehicleAccessException.DATE_NOT_EXIST.newInstance("相机不存在");
        }
        plateDetectContext.setCameraEntity(cameraEntity);

        // 调用核心识别处理逻辑
        try {
            doReceivePlateDetected(plateDetectContext);
            plateDetectContext.setIsOpen(true);
            return true;
        } catch (VehicleAccessException e) {
            // 捕获业务异常：若为台账异常则特殊返回 true，表示流程执行完但记录失败
            log.error("[PlateDetectedBizImpl receivePlateDetected] 车辆核心逻辑处理异常", e);
            if (e.getDefineCode().equals(VehicleAccessException.LEDGER_ERROR.getDefineCode())) {
                plateDetectContext.setIsOpen(true);
                return true;
            } else {
                plateDetectContext.setIsOpen(false);
                return false;
            }
        } catch (Exception e) {
            // 捕获系统异常，标记处理失败
            log.error("[PlateDetectedBizImpl receivePlateDetected] 系统异常", e);
            plateDetectContext.setIsOpen(false);
            return false;
        } finally {
            PlateDetectResultEvent plateDetectResultEvent = new PlateDetectResultEvent(this, plateDetectContext);
            publisher.publishEvent(plateDetectResultEvent);
        }
    }

    private void convertCameraIp(PlateDetectedInternalInfo plateDetectedInternalInfo) {
        String s = systemConfigService.get(SystemConfig.bindCameraIpMap);
        Map<String, String> map = JSONObject.parseObject(s, Map.class);

        if (map.containsKey(plateDetectedInternalInfo.getInternalCameraInfo().getIp())) {
            plateDetectedInternalInfo.getInternalCameraInfo().setIp(map.get(plateDetectedInternalInfo.getInternalCameraInfo().getIp()));
        }
    }


    /**
     * 执行车牌识别核心业务逻辑
     * <p>根据车辆类型选择对应策略进行进出场处理，并记录台账信息。</p>
     *
     * @param plateDetectContext 识别上下文对象
     */
    private void doReceivePlateDetected(PlateDetectContext plateDetectContext) {
        // 根据车辆类型动态获取对应的识别处理支持类（策略模式）
        PlateDetectSupport plateDetectSupport = plateDetectSupportFactory.getSupport(plateDetectContext.getVehicleType());

        // 判断当前为进场或出场识别，调用对应的业务处理逻辑
        if (InOutTypeEnum.IN.name().equals(plateDetectContext.getInOutType())) {
            plateDetectSupport.processIn(plateDetectContext);
        } else {
            plateDetectSupport.processOut(plateDetectContext);
        }

        // 记录车辆进出通信台账，用于后续追溯
        try {
            if (InOutTypeEnum.IN.name().equals(plateDetectContext.getInOutType())) {
                plateDetectSupport.recordIn(plateDetectContext);
            } else {
                plateDetectSupport.recordOut(plateDetectContext);
            }
        } catch (Exception e) {
            // 若台账记录异常，则抛出业务异常 LEDGER_ERROR
            log.error("台账记录异常", e);
            throw VehicleAccessException.LEDGER_ERROR.newInstance("台账记录异常");
        }
    }

    /**
     * 校验车牌号是否符合识别规则
     * <p>过滤被系统忽略的车牌号，并从数据库加载车辆信息。</p>
     *
     * @param vehicleNumber 车牌号
     * @return 对应的普通车辆实体
     */
    private CommonVehicleEntity checkVehicleNumber(String vehicleNumber) {
        // 根据车牌号从数据库获取车辆信息
        CommonVehicleEntity commonVehicleEntity = commonVehicleService.getCommonVehicleByVehicleNumber(vehicleNumber);

        String ignoreVehicleNumberRule = systemConfigService.get(SystemConfig.ignoreVehicleNumber);

        // 检查车牌是否匹配忽略规则（例如测试车、内部车辆）
        if (vehicleNumber.matches(ignoreVehicleNumberRule)) {
            // 若车牌被忽略，则直接抛出异常中断识别逻辑
            throw VehicleAccessException.SYSTEM_ERROR.newInstance("vehicleNumber is ignore vehicleNumber");
        }

        return commonVehicleEntity;
    }


    @Override
    public void receiveHeartBeat(HeartBeatInternalInfo heartBeatInternalInfo) {

        ThreadUtil.sleep(1000);
        //后续逻辑
    }

    @Override
    public void receiveSnapshot(SnapshotInternalInfo snapshotInternalInfo) {

        //保存图片到本地 生成url
        CameraEntity cameraEntity = cameraService.getCameraByIp(snapshotInternalInfo.getIp());
        if (cameraEntity == null) {
            log.error("处理截图数据,相机不存在");
            return;
        }

        if (!WorkModelEnum.SNAP.name().equals(cameraEntity.getWorkMode())) {
            return;
        }

        String url = Base64ToImageUtil.saveBase64(snapshotInternalInfo.getImageFile(), Constants.PICTURE_FILE_SNAP_PATH);


        //再把图片url 和 图片对应的时间保存到数据库
        cameraSnapshotService.storeSnapshotImage(url, snapshotInternalInfo.getIp());
    }

    @Override
    public void receiveGIoIn(GIoInInternalInfo gIoInInternalInfo) {

        //判断这一次是抬杆还是落杆
        Integer openValue = systemConfigService.get(SystemConfig.gIoInValueStandard);
        Integer source = systemConfigService.get(SystemConfig.gIoInSourceStandard);

        if (!source.equals(gIoInInternalInfo.getSource())) {
            return;
        }


        boolean openFlag = Objects.equals(gIoInInternalInfo.getValue(), openValue);


        //获取上一次的识别记录
        PlateDetectContext plateDetectContext = lastPlateDetectedResultCache.get(gIoInInternalInfo.getIp());

        if (plateDetectContext == null) {
            log.error("处理GIoIn数据,未找到上一次识别结果");
            return;
        }

        if (openFlag) {
            plateDetectContext.setUpTime(new Date());
        } else {
            // 释放锁
            CameraSemaphore.release(plateDetectContext.getCameraEntity().getCameraIp());
            plateDetectContext.setDownTime(new Date());
        }

        plateDetectContext.setGIoInType(openFlag ? GIoInTypeEnum.UP : GIoInTypeEnum.DOWN);


        // 发送抬杆/落杆事件
        GIoInResultEvent gIoInResultEvent = new GIoInResultEvent(this, plateDetectContext);
        publisher.publishEvent(gIoInResultEvent);

    }
}
